package com.ruoyi.common.core.redis;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import org.springframework.stereotype.Component;

/**
 * 内存缓存工具类，用于替代 Redis。
 * 保持与原 RedisCache 相同的方法签名，尽量兼容现有调用。
 */
@SuppressWarnings(value = { "unchecked", "rawtypes" })
@Component
public class RedisCache
{
    private static final class CacheValue
    {
        final Object value;
        volatile long expireAtMillis; // <=0 表示不过期

        CacheValue(Object value, long expireAtMillis)
        {
            this.value = value;
            this.expireAtMillis = expireAtMillis;
        }
    }

    private final ConcurrentMap<String, CacheValue> valueStore = new ConcurrentHashMap<>();
    private final ConcurrentMap<String, ConcurrentMap<String, Object>> hashStore = new ConcurrentHashMap<>();
    private final ConcurrentMap<String, Set<Object>> setStore = new ConcurrentHashMap<>();
    private final ConcurrentMap<String, List<Object>> listStore = new ConcurrentHashMap<>();

    private boolean isExpired(CacheValue cacheValue)
    {
        return cacheValue != null && cacheValue.expireAtMillis > 0 && System.currentTimeMillis() > cacheValue.expireAtMillis;
    }

    private void purgeIfExpired(String key)
    {
        CacheValue cv = valueStore.get(key);
        if (isExpired(cv))
        {
            valueStore.remove(key, cv);
        }
    }

    public <T> void setCacheObject(final String key, final T value)
    {
        valueStore.put(key, new CacheValue(value, -1));
    }

    public <T> void setCacheObject(final String key, final T value, final Integer timeout, final TimeUnit timeUnit)
    {
        long ttlMs = timeUnit == null ? 0 : timeUnit.toMillis(timeout == null ? 0 : timeout.longValue());
        long expireAt = ttlMs > 0 ? System.currentTimeMillis() + ttlMs : -1;
        valueStore.put(key, new CacheValue(value, expireAt));
    }

    public boolean expire(final String key, final long timeout)
    {
        return expire(key, timeout, TimeUnit.SECONDS);
    }

    public boolean expire(final String key, final long timeout, final TimeUnit unit)
    {
        CacheValue cv = valueStore.get(key);
        if (cv == null)
        {
            return false;
        }
        long ttlMs = unit.toMillis(timeout);
        cv.expireAtMillis = ttlMs > 0 ? System.currentTimeMillis() + ttlMs : -1;
        return true;
    }

    public long getExpire(final String key)
    {
        CacheValue cv = valueStore.get(key);
        if (cv == null || cv.expireAtMillis <= 0)
        {
            return -1L;
        }
        long remaining = cv.expireAtMillis - System.currentTimeMillis();
        return remaining <= 0 ? -2L : TimeUnit.MILLISECONDS.toSeconds(remaining);
    }

    public Boolean hasKey(String key)
    {
        purgeIfExpired(key);
        return valueStore.containsKey(key);
    }

    public <T> T getCacheObject(final String key)
    {
        purgeIfExpired(key);
        CacheValue cv = valueStore.get(key);
        return cv == null ? null : (T) cv.value;
    }

    public boolean deleteObject(final String key)
    {
        valueStore.remove(key);
        hashStore.remove(key);
        setStore.remove(key);
        listStore.remove(key);
        return true;
    }

    public boolean deleteObject(final Collection collection)
    {
        if (collection == null || collection.isEmpty())
        {
            return false;
        }
        boolean removed = false;
        for (Object k : collection)
        {
            removed |= deleteObject(String.valueOf(k));
        }
        return removed;
    }

    public <T> long setCacheList(final String key, final List<T> dataList)
    {
        if (dataList == null)
        {
            listStore.remove(key);
            return 0L;
        }
        listStore.put(key, new ArrayList<>(dataList));
        return dataList.size();
    }

    public <T> List<T> getCacheList(final String key)
    {
        List<Object> list = listStore.get(key);
        return list == null ? Collections.emptyList() : (List<T>) new ArrayList<>(list);
    }

    public <T> Set<T> getCacheSet(final String key)
    {
        Set<Object> set = setStore.get(key);
        return set == null ? Collections.emptySet() : (Set<T>) set;
    }

    public <T> void setCacheMap(final String key, final Map<String, T> dataMap)
    {
        if (dataMap == null)
        {
            hashStore.remove(key);
        }
        else
        {
            hashStore.put(key, new ConcurrentHashMap<>((Map) dataMap));
        }
    }

    public <T> Map<String, T> getCacheMap(final String key)
    {
        Map<String, Object> map = hashStore.get(key);
        return map == null ? Collections.emptyMap() : (Map) new ConcurrentHashMap<>(map);
    }

    public <T> void setCacheMapValue(final String key, final String hKey, final T value)
    {
        hashStore.computeIfAbsent(key, k -> new ConcurrentHashMap<>()).put(hKey, value);
    }

    public <T> T getCacheMapValue(final String key, final String hKey)
    {
        Map<String, Object> map = hashStore.get(key);
        return map == null ? null : (T) map.get(hKey);
    }

    public <T> List<T> getMultiCacheMapValue(final String key, final Collection<Object> hKeys)
    {
        Map<String, Object> map = hashStore.get(key);
        if (map == null || hKeys == null)
        {
            return Collections.emptyList();
        }
        return (List<T>) hKeys.stream().map(h -> map.get(String.valueOf(h))).collect(Collectors.toList());
    }

    public boolean deleteCacheMapValue(final String key, final String hKey)
    {
        Map<String, Object> map = hashStore.get(key);
        if (map == null)
        {
            return false;
        }
        return map.remove(hKey) != null;
    }

    public Collection<String> keys(final String pattern)
    {
        if (pattern == null || pattern.isEmpty())
        {
            return Collections.emptyList();
        }
        String prefix = pattern.endsWith("*") ? pattern.substring(0, pattern.length() - 1) : pattern;
        return valueStore.keySet().stream()
                .filter(k -> k.startsWith(prefix))
                .collect(Collectors.toList());
    }
}
